package com.fenquen.dfs.storage.controller;

import com.alibaba.fastjson.JSON;
import com.fenquen.dfs.base.model.AjaxResponse;
import com.fenquen.dfs.base.protocol.Protocol;
import com.fenquen.dfs.base.protocol.data.storage_source.req_from_storage.Report;
import com.fenquen.dfs.storage.config.DFSStorageConfig;
import com.fenquen.dfs.storage.exception.File404Exception;
import com.fenquen.dfs.storage.exception.FileSaveException;
import com.fenquen.dfs.storage.http.HttpUtils;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramPacket;
import org.apache.http.HttpResponse;
import org.apache.http.concurrent.FutureCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;

@Controller
public class Portal {
    private static final Logger LOGGER = LoggerFactory.getLogger(Portal.class);

    @Autowired
    private Channel channel;

    @Autowired
    private DFSStorageConfig dfsStorageConfig;

    @RequestMapping(value = "upload_", produces = "application/json;charset=utf-8")
    @ResponseBody
    public String upload_(@RequestParam("file") MultipartFile uploadFile,
                          @RequestParam String uploadFileUUID,
                          @RequestParam String replicaStorageNodes,
                          @RequestParam String extName,
                          @RequestParam String originFileName,
                          HttpServletResponse response) {

        Throwable error = null;
        try {
            uploadFile.transferTo(Paths.get(dfsStorageConfig.storagePath,
                    uploadFileUUID + "." + extName));
        } catch (Exception e) {
            FileSaveException fileSaveException = new FileSaveException(e);
            LOGGER.error(fileSaveException.getMessage(), fileSaveException);
            error = fileSaveException;

        }

        String resultJsonStr =
                null != error ? JSON.toJSONString(AjaxResponse.fail(error)) :
                        JSON.toJSONString(AjaxResponse.success(uploadFileUUID));

        // 生成Report生成byte[]通过组播发送到router
        Protocol protocol = assembleReport(error, uploadFileUUID, originFileName, extName);
        channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(protocol.getByteArr()), dfsStorageConfig.multicastAddress));

        if (null != error) {
            return JSON.toJSONString(AjaxResponse.fail(error));
        }

        // 向其它的replica存储节点发送http请求,它们接收到之后再从本节点下载之前上传的文件以实现文件的replica
        if (!StringUtils.isEmpty(replicaStorageNodes)) {
            for (String replicaNode : replicaStorageNodes.split(",")) {
                String url = replicaNode + "/sync_?" +
                        "srcStorageNode=" + "http://" + dfsStorageConfig.serverAddress + ":" + dfsStorageConfig.serverPort +
                        "uploadFileUUID=" + uploadFileUUID +
                        "&extName=" + extName;
                HttpUtils.post(url, null, new FutureCallback<HttpResponse>() {

                    @Override
                    public void completed(HttpResponse httpResponse) {

                    }

                    @Override
                    public void failed(Exception e) {

                    }

                    @Override
                    public void cancelled() {

                    }
                });
            }
        }

        // cookie内容的格式 地址,格式名字,原始名字
        Cookie cookie = new Cookie(uploadFileUUID,
                dfsStorageConfig.serverAddress + ":" + dfsStorageConfig.serverPort + "@"
                        + extName + "@" +
                        originFileName);
        cookie.setMaxAge(60);
        response.addCookie(cookie);

        return resultJsonStr;
    }

    @RequestMapping("sync_")
    public void sync_(String srcStorageNode,
                      String uploadFileUUID,
                      String extName) {
        // 下载文件
        String url = srcStorageNode + "/download4Sync_?uploadFileUUID=" + uploadFileUUID + "&extName=" + extName;
        HttpUtils.get(url, new FutureCallback<HttpResponse>() {
            @Override
            public void completed(HttpResponse httpResponse) {
                Throwable error = null;
                try {
                    FileOutputStream fileOutputStream =
                            new FileOutputStream(new File(dfsStorageConfig.storagePath, uploadFileUUID + "." + extName));
                    httpResponse.getEntity().writeTo(fileOutputStream);
                } catch (Exception e) {
                    error = e;
                    LOGGER.error(e.getMessage(), e);
                }

                Protocol protocol = assembleReport(error, uploadFileUUID, uploadFileUUID, extName);
                channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(protocol.getByteArr()), dfsStorageConfig.multicastAddress));
            }

            @Override
            public void failed(Exception e) {
                LOGGER.error(e.getMessage(), e);
            }

            @Override
            public void cancelled() {

            }
        });
    }

    @RequestMapping(value = "/download4Sync_")
    public void download4Sync_(HttpServletResponse response,
                               String uploadFileUUID,
                               String extName) throws Exception {
        // 是用来同步的能保证文件一定是存在的
        File file = new File(dfsStorageConfig.storagePath, uploadFileUUID + "." + extName);
        response.setHeader("content-type", "application/octet-stream");
        response.setHeader("content-disposition", "attachment;filename=" + uploadFileUUID + "." + extName);
        response.setHeader("content-length", file.length() + "");

        FileInputStream fileInputStream = new FileInputStream(file);

        byte[] bytes = new byte[8192];
        int a;
        ServletOutputStream servletOutputStream = response.getOutputStream();

        while ((a = fileInputStream.read(bytes)) != -1) {
            servletOutputStream.write(bytes, 0, a);
        }

        servletOutputStream.close();
        fileInputStream.close();
    }

    // 成功应该下载文件失败返回json文本
    @RequestMapping(value = "/download_")
    private void download_(HttpServletResponse response,
                           String uploadFileUUID,
                           String replicaStorageNodes,
                           String originFileName,
                           String extName) throws Exception {
        // 目标文件
        File file = new File(dfsStorageConfig.storagePath, uploadFileUUID + "." + extName);

        if (!file.exists()) {
            if (StringUtils.isEmpty(replicaStorageNodes)) {
                File404Exception file404Exception = new File404Exception("uploadFileUUID:" + uploadFileUUID);
                LOGGER.error(file404Exception.getMessage(), file404Exception);
                throw file404Exception;
            }

            String[] arr = replicaStorageNodes.split(",");
            replicaStorageNodes = "";
            for (int a = 1; arr.length > a; a++) {
                replicaStorageNodes += arr[a] + ",";
            }

            String url = arr[0] + "/download_" +
                    "?uploadFileUUID=" + uploadFileUUID +
                    "&replicaStorageNodes=" + replicaStorageNodes +
                    "&originFileName=" + URLEncoder.encode(originFileName, "utf-8") +
                    "&extName=" + extName;

            response.sendRedirect(url);
            return;
        }

        response.setHeader("content-type", "application/octet-stream");
        response.setHeader("content-disposition",
                "attachment;filename=" + URLEncoder.encode(originFileName, "utf-8"));
        response.setHeader("content-length", file.length() + "");

        FileInputStream fileInputStream = new FileInputStream(file);

        byte[] bytes = new byte[8192];
        int a;
        ServletOutputStream servletOutputStream = response.getOutputStream();

        while ((a = fileInputStream.read(bytes)) != -1) {
            servletOutputStream.write(bytes, 0, a);
        }

        servletOutputStream.close();
        fileInputStream.close();
    }


    @RequestMapping(value = "/delete_", produces = "application/json;charset=utf-8")
    @ResponseBody
    public String delete_(HttpServletResponse response,
                          String uploadFileUUID,
                          String replicaStorageNodes,
                          String originFileName,
                          String extName) {
        // 目标文件
        File file = new File(dfsStorageConfig.storagePath, uploadFileUUID + "." + extName);
        file.delete();

        //生成report并通过组播上报到router
        Protocol protocol = assembleReport(null, uploadFileUUID, uploadFileUUID, extName);
        channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(protocol.getByteArr()), dfsStorageConfig.multicastAddress));

        // 要是文件没有保存在其他的存储节点直接结束
        if (StringUtils.isEmpty(replicaStorageNodes)) {
            return JSON.toJSONString(AjaxResponse.PASS);
        }

        // 向其他的replicaStorageNodes的/delete_发送http请求
        for (String replicaStorageNode : replicaStorageNodes.split(",")) {
            String url = replicaStorageNode + "/delete_?uploadFileUUID=" + uploadFileUUID + "&extName=" + extName;
            HttpUtils.get(url, new FutureCallback<HttpResponse>() {

                @Override
                public void completed(HttpResponse httpResponse) {

                }

                @Override
                public void failed(Exception e) {

                }

                @Override
                public void cancelled() {

                }
            });
        }

        return JSON.toJSONString(AjaxResponse.PASS);

    }

    private Protocol assembleReport(Throwable error,
                                    String uploadFileUUID,
                                    String originFileName,
                                    String extName) {
        Report report = new Report();

        report.uploadFileUUID = uploadFileUUID;
        report.flag = Report.Flag.添加;
        report.success = (null != error ? Report.Success.失败 : Report.Success.成功);

        if (error != null) {
            report.msgLength = error.getMessage().length();
            report.msg = error.getMessage();
        } else {
            report.msgLength = 0;
        }

        report.originFileNameLength = originFileName.getBytes(StandardCharsets.UTF_8).length;
        report.originFileName = originFileName;

        report.extNameLength = extName.length();
        report.extName = extName;

        report.host = dfsStorageConfig.serverAddress;
        report.port = dfsStorageConfig.serverPort;

        Protocol protocol = new Protocol();
        protocol.protocolType = Protocol.ProtocolType.ReqFromStorage.存储节点文件上传回执;
        protocol.dataLength = (short) report.getByteLength();
        protocol.data = report;

        LOGGER.info("assembleReport,{}", JSON.toJSONString(protocol));
        return protocol;
    }
}
